package main

import (
	"container/list"
	"flag"
	"fmt"
	"github.com/PuerkitoBio/goquery"
	"github.com/henrylee2cn/goutil"
	"log"
	"math/rand"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"strconv"
	"strings"
	"studygo/other/picutils"
	"sync"
	"time"
)

func init() {
	// 我们一般使用系统时间的不确定性来进行初始化
	rand.Seed(time.Now().UnixNano())
	log.SetPrefix("TRACE: ")
	log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
}

type picBean struct {
	name        string
	picCount    int
	firstPicUrl string
}

var (
	wg sync.WaitGroup
	//baseUrl     = "https://www.invshen.net"网址变了
	baseUrl     = "https://www.invshen.net"
	picSavePath = "d:/tmp/psnv/gallery/"
	picChan     = make(chan *picBean, 50)
	//采集多少页
	picPageNum int
	//图片集合
	listPic *list.List
)

func main() {
	mainUrlCMD := flag.String("url", "https://www.invshen.net/gallery/",
		"图片链接地址页|(https://www.invshen.net/girl/22162/)或者(https://www.invshen.net/gallery/)")
	picSavePathCMD := flag.String("s", picSavePath, "下载保存位置")
	picPageNumCMD := flag.Int("n", 9, "下载截止页码(https://www.invshen.net/gallery/类型的地址需要)")
	flag.Parse()
	picSavePath = picutils.PathDo(*picSavePathCMD)
	mainUrl := picutils.PathDo(*mainUrlCMD)
	picPageNum = *picPageNumCMD
	listPic = list.New() // 创建list对象

	startTime := time.Now()
	log.Println("下载地址和保存位置是:", mainUrl, picSavePath)
	if strings.Contains(mainUrl, "gallery") {
		go collectPicGallery(mainUrl, picPageNum)
	} else {
		go collectPic(mainUrl, picChan)
	}

	downFromChan(picChan)
	wg.Wait()
	endTime := time.Now()

	log.Println("开始时间|结束时间", startTime.Format("2006-01-02 15:04:05"), endTime.Format("2006-01-02 15:04:05"))

	/*allFiles, err := picutils.GetAllFiles(picSavePath)
	if err == nil {
		for _, file := range allFiles {
			if picutils.GetFileSize(file) == 0 {
				os.Remove(file)
			}
		}
	}*/
	for i := listPic.Front(); i != nil; i = i.Next() {
		filePath := fmt.Sprint(i.Value)
		//删除50K的文件
		if picutils.GetFileSize(filePath)/1024 < 50 {
			os.Remove(filePath)
		}
	}

	//all := strings.ReplaceAll(picSavePath, "/", "\\")
	picutils.ExecuteCmdOnly("explorer", filepath.FromSlash(picSavePath))
}

// 收集图片到管道
func collectPic(url string, picChan chan *picBean) {
	resp, err := http.Get(url)
	if err != nil {
		log.Println(url, "访问失败")
		return
	}
	doc, err := goquery.NewDocumentFromReader(resp.Body)
	if err != nil {
		log.Println(url, "err:", err)
		return
	}
	findLen := doc.Find(".archive_more a").Nodes
	if len(findLen) == 0 {
		//只有当前页面这部分,不用翻页	去查找
		getSinalePic(doc)
	} else {
		albumText := doc.Find(".archive_more a").First().Text()
		log.Println("-->", albumText, url)
		i, b := picutils.GetInt(albumText)
		if !b {
			getAlbumPic(url, i)
		}
	}
	close(picChan)
}

//提取图片|https://www.invshen.net/girl/24633/
// i 表示 总 数量 比如这个是 41
func getAlbumPic(url string, count int) {
	//https://www.invshen.net/girl/24633/album/2.html
	log.Println("是 album 类型页面 ", url)
	for i := 1; i <= (count+29)/30; i++ {
		strurl := url + "album/" + strconv.Itoa(i) + ".html"
		log.Println("-------", strurl)
		getAlbumDetail(strurl)
	}
}

//解析 https://www.invshen.net/girl/24633/album/
func getAlbumDetail(albumUrl string) {
	doc, err := picutils.HttpGetByUrl(albumUrl)
	if err != nil {
		log.Println("解析album失败")
		return
	}
	doc.Find(".photo_ul .igalleryli .igalleryli_div .igalleryli_link").Each(func(i int, s *goquery.Selection) {
		attrHref, exists := s.Attr("href")
		if exists {
			parsePicByUrl(baseUrl + attrHref)
		}
	})
}

// 当前页面只有1页 ，类似于 https://www.invshen.net/girl/27898/
func getSinalePic(doc *goquery.Document) {
	doc.Find(".photo_ul .igalleryli_div .igalleryli_link").Each(func(i int, s *goquery.Selection) {
		attr, _ := s.Attr("href")
		parsePicByUrl(baseUrl + attr)
	})
}

//从管道中获取并下载图片
func downFromChan(picChan <-chan *picBean) {
	log.Println("下载开始~~")
	for {
		if data, ok := <-picChan; ok {
			wg.Add(1)
			go downPicByPicBeanByCmd(data)
		} else {
			break
		}
	}
}

// 解析页面|地址为 https://www.invshen.net/gallery/qingchun/类型
func collectPicGallery(url string, picPageNum int) {
	var (
		getUrl string
	)
	for i := 1; i <= picPageNum; i++ {
		if i == 1 {
			getUrl = url
		} else {
			getUrl = url + strconv.Itoa(i) + ".html"
		}
		doc, err := picutils.HttpGetByUrl(getUrl)
		if err != nil {
			log.Println("访问失败", getUrl, err)
			return
		}
		doc.Find("#listdiv .galleryli .galleryli_div a").Each(func(i int, s *goquery.Selection) {
			attr, _ := s.Attr("href")
			parsePicByUrl(baseUrl + attr)
		})
	}
	close(picChan)
}

// 解析 图片url(https://www.invshen.net/g/34020/)
func parsePicByUrl(url string) {
	resp, err := http.Get(url)
	if err != nil {
		log.Println("地址失败", url)
	}
	doc, err := goquery.NewDocumentFromReader(resp.Body)
	if err != nil {
		return
	}
	text := doc.Find("#dinfo span").Text()
	//第一个图片地址|https://img.onvshen.com:85/gallery/27898/34020/s/0.jpg
	var first = doc.Find("#hgallery img").First()
	firstPicURL, _ := first.Attr("src")
	//有页面不存在的情况
	if len(text) > 0 {
		pageCount, b := picutils.GetInt(text)
		if !b {
			split := strings.Split(url, "/")
			picBean := &picBean{name: split[len(split)-2] + "_" + strconv.Itoa(pageCount), picCount: pageCount, firstPicUrl: firstPicURL}
			log.Println(picBean)
			picChan <- picBean
		}
	}

}

func downPicByPicBeanByCmd(bean *picBean) {
	defer wg.Done()
	index := strings.LastIndex(bean.firstPicUrl, "/")
	strBaseUrl := bean.firstPicUrl[:index+1] //文件基本名字(除去最后扩展名)
	picExt := path.Ext(bean.firstPicUrl)     //文件扩展名

	//picutils.MkdirFileAll(picSavePath, bean.name)
	var downPicUrl string
	log.Println(bean)

	//下载失败数
	var errNum = 0
	for i := 0; i < bean.picCount; i++ {
		if i == 0 {
			downPicUrl = strBaseUrl + strconv.Itoa(i) + picExt
		} else {
			downPicUrl = strBaseUrl + fmt.Sprintf("%03d", i) + picExt
		}
		time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
		f := picSavePath + bean.name + "_" + fmt.Sprintf("%03d", i) + picExt
		if !goutil.FileExists(f) {
			listPic.PushBack(f)
			//TODO 证书被吊销
			err := picutils.ExecuteCmd("curl", "--connect-timeout", "10", "-m", "20", "--ssl-no-revoke", "--referer", baseUrl, downPicUrl, "-o", f)
			if !err {
				errNum++
			}
		}
	}
	//	如果文件夹为空，则删除
	if picutils.DirIsEmpty(picSavePath + bean.name) {
		log.Println("文件夹为空", picSavePath+bean.name)
		os.Remove(picSavePath + bean.name)
	}
}
